/******************************************************************************
 *
 * MotionViewer
 * Copyright (C) 2007 Sangyoon Lee, Electronic Visualization Laboratory, 
 * University of Illinois at Chicago
 *
 * This software is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either Version 2.1 of the License, or 
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
 * License for more details.
 * 
 * You should have received a copy of the GNU Lesser Public License along
 * with this software; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Questions or comments about MotionViewer should be directed to 
 * cavern@evl.uic.edu
 *
 * Original Source of XMLParser utility:
 *       Ahmad Hassan
 *       http://www.codeproject.com/cpp/XMLParser.asp
 *
 * Source of pretty format printing module: 
 *       (C) 2005 Axel Weiss (GNU Lesser General Public License)
 *       http://www.mail-archive.com/xerces-c-dev@xml.apache.org/msg15427.html
 *
 *****************************************************************************/


/*
* -----------------------------------------------------------------------------
* Filename: LLXMLParser.cpp
* -----------------------------------------------------------------------------
* Notes:    
* -----------------------------------------------------------------------------
*/

#pragma warning(disable:4101)
#pragma warning(disable:4996)
#pragma warning(disable:4267)
#pragma warning(disable:4244)


#include <xercesc/util/XMLUniDefs.hpp>

#include "LLXMLParser.h"
#include "LLXStr.h"

#include <stdio.h>

static bool is_text_node_whitespace_only(const DOMNode *node){
	if (node->getNodeType() == DOMNode::TEXT_NODE){
		return XMLString::isAllWhiteSpace(node->getNodeValue());
	}
	return false;
}

static bool is_last_child(const DOMNode *node){
	const DOMNode *n = node->getNextSibling();
	if (!n) return true;
	if (is_text_node_whitespace_only(n)) return is_last_child(n);
	return false;
}

static void remove_ws_nodes(DOMNode *node){
	if (node->getNodeType() == DOMNode::ELEMENT_NODE){
		// remove all leading ws-only nodes:
		for (DOMNode *child=node->getFirstChild(); child; child=node->getFirstChild()){
			if (!is_text_node_whitespace_only(child)) break;
			node->removeChild(child)->release();
		}
		for (DOMNode *child=node->getFirstChild(); child; child=child->getNextSibling()){
			DOMNode *prev = child->getPreviousSibling();
			if (is_text_node_whitespace_only(child)){
				// since we have no leading ws-only nodes (just removed them all),
				// predessor of child exists:
				DOMNode *n = child;
				child = prev;
				node->removeChild(n)->release();
			}
			// recursively remove ws-only nodes of all children:
			remove_ws_nodes(child);
		}
	}
}

static void concat(XMLCh *result, const XMLCh *s1, unsigned l1, const XMLCh *s2, unsigned l2){
	unsigned i;
	for (i=0; i<l1; ++i){
		result[i] = s1[i];
	}
	for (i=0; i<l2; ++i){
		result[l1 + i] = s2[i];
	}
	result[l1 + l2] = chNull;
}

static void make_pretty_print(DOMNode *node, const XMLCh *leading_indent=0, unsigned leading_size=0){

	if (node->getNodeType() == DOMNode::ELEMENT_NODE){
		XMLCh* new_indent = new XMLCh[leading_size + indent_size + 1];
		concat(new_indent, leading_indent, leading_size, indent_string, indent_size);
		XMLCh* begin = new XMLCh[leading_size + indent_size + newline_size + 1];
		concat(begin, newline_string, newline_size, new_indent, leading_size + indent_size);
		for (DOMNode *child=node->getFirstChild(); child; child=child->getNextSibling()){
			if (child->getNodeType() == DOMNode::ELEMENT_NODE){
				DOMNode *n = node->getOwnerDocument()->createTextNode(begin);
				node->insertBefore(n, child);
				make_pretty_print(child, new_indent, leading_size + indent_size);
			}
		}
		if (node->getLastChild() && node->getLastChild()->getNodeType() == DOMNode::ELEMENT_NODE){
			XMLCh* end = new XMLCh[leading_size + newline_size + 1];
			concat(end, newline_string, newline_size, leading_indent, leading_size);
			DOMNode *n = node->getOwnerDocument()->createTextNode(end);
			node->appendChild(n);
			XMLString::release(&end);
		}

		XMLString::release(&new_indent);
		XMLString::release(&begin);

	}
	if (!leading_size) node->getOwnerDocument()->normalize();

}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
LLXMLParser::LLXMLParser()
{
	m_pDoc = NULL;
	m_pImpl = NULL;
	m_pRootElem = NULL;
}

LLXMLParser::~LLXMLParser()
{

	if (m_pDoc != NULL)
		terminate();
}

// Initializing the DOM Implementations using "Core "
bool LLXMLParser::initialize()
{
	try
    {
        XMLPlatformUtils::Initialize();
		m_pImpl = DOMImplementationRegistry::getDOMImplementation(X("Core"));
	}

	catch(const XMLException& toCatch)
    {
       return false;	// Returning 0 
    }
	return true;

}


// Node Tag = Parent Node
// Node Name = Name of the New Node to Created 
// data is the Data that u want to store in the Text Area of the Node 
// DATA_TYPE = Information abt the the Data (DATA_INT , DATA_FLOAT , DATA_CHAR , DATA_ELEMENTONLY)
// len = Length of array in case of Floats or Integers (1 based Index) by default its 0 (Single Elemnt)
DOMElement* LLXMLParser::addElement(char *node_tag, char *node_name, void *data, char DATA_TYPE,int len)
{
	if (m_pDoc == NULL || m_pImpl == NULL)
		return NULL;
	XMLCh *tag = XMLString::transcode(node_tag);
	DOMNodeList * node_list = m_pDoc->getElementsByTagName(tag);
	XMLString::release(&tag);
	tag = XMLString::transcode(node_name);
	DOMElement	* element  = m_pDoc->createElement(tag); //
	XMLString::release(&tag);
	DOMElement  * temp_element = NULL;
	DOMText		* Val = NULL;
	int length = node_list->getLength();
	
	if (length==0)
	{
		m_pRootElem->appendChild(element);
	}
	else
	{
		DOMNode *	node = node_list->item(0);
	    node->appendChild(element);
	}
	switch (DATA_TYPE)
	{
		case DATA_INT:
		{
				int *temp = (int*)data;
				char char_temp[10]; 
			 	
				for (int x=0;x<len;x++)
				{
					if (len>1)
					{

						strcpy(char_temp,"ARR");
						//itoa(x+1,&char_temp[3],10);
						sprintf(&char_temp[3], "%d", x+1);
						tag = XMLString::transcode(char_temp);
						temp_element = m_pDoc->createElement(tag);
						XMLString::release(&tag);
						element->appendChild(temp_element);
						//itoa(temp[x],char_temp,10);
						sprintf(char_temp, "%d", temp[x]);
						tag = XMLString::transcode(char_temp);
						Val = m_pDoc->createTextNode(tag);
						XMLString::release(&tag);
						temp_element->appendChild(Val);

					}
					else
					{
						//itoa(*temp,char_temp,10);
						sprintf(char_temp, "%d", *temp);
						tag = XMLString::transcode(char_temp);
						Val = m_pDoc->createTextNode(tag);
						XMLString::release(&tag);
						element->appendChild(Val);
					}
				}
				break;
		}
	
		case DATA_CHAR:
		{
			char *temp = (char*)data;
			tag = XMLString::transcode(temp);
			DOMText*    Val = m_pDoc->createTextNode(XMLString::transcode(temp));
			element->appendChild(Val);
			XMLString::release(&tag);
			break;
		}

		case DATA_FLOAT:
		{
			float *temp = (float*)data;
			char char_temp[10];
			char fstr[16];
			for (int x=0;x<len;x++)
			{
				sprintf(fstr, "%f", temp[x]);
				if (len>1)
				{
					strcpy(char_temp,"ARR");
					//itoa(x+1,&char_temp[3],10);
					sprintf(&char_temp[3], "%d", x+1);
					tag = XMLString::transcode(char_temp);
					temp_element = m_pDoc->createElement(tag);
					XMLString::release(&tag);
					element->appendChild(temp_element);
					//itoa(temp[x],char_temp,10);
					sprintf(char_temp, "%d", temp[x]);
					tag = XMLString::transcode(fstr);
					Val = m_pDoc->createTextNode(tag);
					temp_element->appendChild(Val);
					XMLString::release(&tag);
				}
				else
				{
					//itoa(*temp,char_temp,10);
					sprintf(char_temp, "%d", *temp);
					tag = XMLString::transcode(fstr);
					Val = m_pDoc->createTextNode(tag);
					element->appendChild(Val);
					XMLString::release(&tag);
				}
				memset(fstr, 0, 16);
			}
			break;
		}

		case DATA_ELEMENTONLY:
		{
			break;
		}
	}
	return element;
}

DOMElement* LLXMLParser::addElement(DOMElement* parent, char *node_name, void *data, char DATA_TYPE,int len)
{
	if (m_pDoc == NULL || m_pImpl == NULL)
		return NULL;
	
	if (parent == NULL)
		return NULL;

	XMLCh *tag = XMLString::transcode(node_name);
	DOMElement	* element  = m_pDoc->createElement(tag);
	XMLString::release(&tag);
	DOMElement  * temp_element = NULL;
	DOMText		* Val = NULL;
	
	parent->appendChild(element);

	switch (DATA_TYPE)
	{
		case DATA_INT:
		{
				int *temp = (int*)data;
				char char_temp[10]; 
			 	
				for (int x=0;x<len;x++)
				{
					if (len>1)
					{
						strcpy(char_temp,"ARR");
						//itoa(x+1,&char_temp[3],10);
						sprintf(&char_temp[3], "%d", x+1);
						tag = XMLString::transcode(char_temp);
						temp_element = m_pDoc->createElement(tag);
						XMLString::release(&tag);
						element->appendChild(temp_element);
						//itoa(temp[x],char_temp,10);
						sprintf(char_temp, "%d", temp[x]);
						tag = XMLString::transcode(char_temp);
						Val = m_pDoc->createTextNode(tag);
						XMLString::release(&tag);
						temp_element->appendChild(Val);
					}
					else
					{
						//itoa(*temp,char_temp,10);
						sprintf(char_temp, "%d", *temp);
						tag = XMLString::transcode(char_temp);
						Val = m_pDoc->createTextNode(tag);
						XMLString::release(&tag);
						element->appendChild(Val);

					}
				}
				break;
		}
	
		case DATA_CHAR:
		{
			char *temp = (char*)data;
			tag = XMLString::transcode(temp);
			DOMText*    Val = m_pDoc->createTextNode(XMLString::transcode(temp));
			element->appendChild(Val);
			XMLString::release(&tag);
			break;
		}

		case DATA_FLOAT:
		{
			float *temp = (float*)data;
			char char_temp[10];
			char fstr[16];
			for (int x=0;x<len;x++)
			{
				sprintf(fstr, "%f", temp[x]);
				if (len>1)
				{
					strcpy(char_temp,"ARR");
					//itoa(x+1,&char_temp[3],10);
					sprintf(&char_temp[3], "%d", x+1);
					tag = XMLString::transcode(char_temp);
					temp_element = m_pDoc->createElement(tag);
					XMLString::release(&tag);
					element->appendChild(temp_element);
					//itoa(temp[x],char_temp,10);
					sprintf(char_temp, "%d", temp[x]);
					tag = XMLString::transcode(fstr);
					Val = m_pDoc->createTextNode(tag);
					temp_element->appendChild(Val);
					XMLString::release(&tag);
				}
				else
				{
					//itoa(*temp,char_temp,10);
					sprintf(char_temp, "%d", *temp);
					tag = XMLString::transcode(fstr);
					Val = m_pDoc->createTextNode(tag);
					element->appendChild(Val);
					XMLString::release(&tag);

				}
				memset(fstr, 0, 16);
			}
			break;
		}

		case DATA_ELEMENTONLY:
		{
			break;
		}
	}
	return element;
}

// Creates the Document with a specified tag
bool LLXMLParser::createDocument(const char *data)
{
	XMLCh * tag = XMLString::transcode(data);
	m_pDoc = NULL;
	try
	{
		m_pDoc = m_pImpl->createDocument(
					   0,               // root element namespace URI.
					   tag,				// root element name
					   0);              // document type object (DTD).
		m_pRootElem = m_pDoc->getDocumentElement();
	}
	catch (XMLException &toCatch)
	{
		return false;
	}
	XMLString::release(&tag);
	return true;
}

// Releases the Document Only ... not the Implementation
void LLXMLParser::destroyDoc()
{
	m_pDoc->release();
	m_pDoc = NULL;
}

// Retuens the complete XML Tree in form of a char String
char * LLXMLParser::getXMLString()
{
	// make it pretty format
	makePrettyPrint(m_pRootElem);

	DOMWriter  *theSerializer = ((DOMImplementationLS*)m_pImpl)->createDOMWriter();
	XMLCh *r_data=theSerializer->writeToString(*m_pRootElem);

	#ifdef DEBUG_ON
	{
		WriteToFile("Dump.txt");
	}
	#endif
		
	char *temp = XMLString::transcode(r_data);
	int len = strlen(temp);
	char *c_data = new char[len+1];
	strcpy(c_data,temp);
	c_data[len] = 0;
	theSerializer->release();
	XMLString::release(&temp);

	return c_data;
}

// Creates a Document from a char string
bool LLXMLParser::createDocumentFromString(const char *str)
{
	int len = strlen(str);
	//MemBufInputSource * pSource = new MemBufInputSource((XMLByte *)str, len, L"");
	MemBufInputSource * pSource = new MemBufInputSource((XMLByte *)str, len, XMLStrL(""));
	Wrapper4InputSource source(pSource);	
	DOMBuilder *parser = ((DOMImplementationLS*)m_pImpl)->createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0);
	
	if (m_pDoc)
		m_pDoc->release();
	
	m_pDoc = parser->parse(source);
	m_pDoc->getElementsByTagName(XMLString::transcode("CARE"))->getLength();
	m_pRootElem = m_pDoc->getDocumentElement();

	#ifdef DEBUG_ON
	{
		WriteToFile("Dump.txt");
	}
	#endif
	delete pSource;
	return true;	

}
	
// Returns integer from the Tag specified

int LLXMLParser::getIntFromTag(char *tag)
{
	if (m_pDoc==NULL)
		return NULL;
	DOMNodeList *node_list = m_pDoc->getElementsByTagName(XMLString::transcode(tag));
	DOMNode * node = node_list->item(0);
	if (node == NULL)
		return 0;
	const XMLCh *str = node->getTextContent();

	char* char_str = XMLString::transcode(str);
	int result = atoi(char_str);
	XMLString::release(&char_str);

	return result;
}


// Returns Float from the Tag specified
float LLXMLParser::getFloatFromTag(char *tag)
{
	if (m_pDoc==NULL)
		return NULL;
	DOMNodeList *node_list = m_pDoc->getElementsByTagName(XMLString::transcode(tag));
	DOMNode * node = node_list->item(0);
	if (node == NULL)
		return 0;
	const XMLCh *str = node->getTextContent();

	char* char_str = XMLString::transcode(str);
	float result = atof(char_str);
	XMLString::release(&char_str);
	
	return result;
}

// Returns Char String from the Tag specified
char* LLXMLParser::getCharFromTag(char *tag)
{
	if (m_pDoc == NULL)
		return NULL;
	DOMNodeList *node_list = m_pDoc->getElementsByTagName(XMLString::transcode(tag));
	DOMNode * node = node_list->item(0);
	if (node == NULL)
		return NULL;
	const XMLCh *str = node->getTextContent();

	char* char_str = XMLString::transcode(str);
	char* r_str = NULL;
	int len = strlen(char_str);
	r_str = new char[len+1];
	memcpy(r_str,char_str,len);
	r_str[len] = 0;
	XMLString::release(&char_str);

	return r_str;

}

// Returns integer Array from the Tag specified (automaticaly allocates memory to the number of 
// childs found under that tag
int* LLXMLParser::getIntegerArray(char *tag)
{
	int *arr = NULL;
	char *temp;
	if (m_pDoc==NULL)
		return NULL;
	DOMNodeList * node_list = m_pDoc->getElementsByTagName(XMLString::transcode(tag));
	DOMNodeList * child_list = node_list->item(0)->getChildNodes();
	int len = child_list->getLength();
	DOMNode * node = child_list->item(0);
	if (node == NULL)
		return 0;
	arr = new int[len];
	for (int x=0;x<len;x++)
	{
		XMLCh * c = (XMLCh *)node->getTextContent();
		temp = XMLString::transcode(c);
		arr[x] = atoi(temp);
		XMLString::release(&temp);
		node = node->getNextSibling();
	}
	return arr;

}

// Returns Float Array from the Tag specified (automaticaly allocates memory to the number of 
// childs found under that tag)
float* LLXMLParser::getFloatArray(char *tag)
{
	float *arr = NULL;
	char *temp;
	if (m_pDoc==NULL)
		return NULL;
	DOMNodeList * node_list = m_pDoc->getElementsByTagName(XMLString::transcode(tag));
	DOMNodeList * child_list = node_list->item(0)->getChildNodes();
	int len = child_list->getLength();
	DOMNode * node = child_list->item(0);
	if (node == NULL)
		return 0;
	arr = new float[len];
	for (int x=0;x<len;x++)
	{
		XMLCh * c = (XMLCh *)node->getTextContent();
		temp = XMLString::transcode(c);
		arr[x] = atof(temp);
		XMLString::release(&temp);
		node = node->getNextSibling();
	}
	return arr;

}

// Terminates the Implementation
void LLXMLParser::terminate()
{
	if (m_pDoc)
	{
		m_pDoc->release();
		m_pDoc = NULL;
	}

	XMLPlatformUtils::Terminate();
}

// Creates a Dcoument from a file specfied
void LLXMLParser::createDocumentFromFile(const char *filename)
{
	DOMBuilder *parser = ((DOMImplementationLS*)m_pImpl)->createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0);
	if (m_pDoc)
		m_pDoc->release();
	m_pDoc = parser->parseURI(filename);
	m_pRootElem = m_pDoc->getDocumentElement();
}

void LLXMLParser::writeToFile(const char *filename)
{
	if (m_pDoc == NULL)
		return;
	
	makePrettyPrint(m_pRootElem);

	XMLFormatTarget *myFormTarget;
	DOMWriter  *theSerializer = ((DOMImplementationLS*)m_pImpl)->createDOMWriter();
	myFormTarget = new LocalFileFormatTarget(filename);
	theSerializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true);
	theSerializer->setFeature(XMLUni::fgDOMWRTSplitCdataSections, true);
	theSerializer->writeNode(myFormTarget,*m_pDoc);
	theSerializer->release();
	delete myFormTarget;
}

void LLXMLParser::addAttribute(DOMElement* element, char* att_name, char* value)
{
	element->setAttribute(X(att_name), X(value));
}

void LLXMLParser::addFloatAttribute(DOMElement* element, char* att_name, float value)
{
	char str[16];
	sprintf(str, "%f", value);
	element->setAttribute(X(att_name), X(str));
}

void LLXMLParser::addIntAttribute(DOMElement* element, char* att_name, int value)
{
	char str[16];
	sprintf(str, "%i", value);
	element->setAttribute(X(att_name), X(str));
}

void LLXMLParser::addCharAttribute(DOMElement* element, char* att_name, char* value)
{
	element->setAttribute(X(att_name), X(value));
}

float LLXMLParser::getFloatAttribute(DOMElement* element, char* att_name)
{
	const XMLCh *att = element->getAttribute(X(att_name));
	char* str = XMLString::transcode(att);
	float result = atof(str);
	XMLString::release(&str);

	return result;
}

int LLXMLParser::getIntAttribute(DOMElement* element, char* att_name)
{
	const XMLCh *att = element->getAttribute(X(att_name));
	int result = XMLString::parseInt(att);

	return result;

}

char* LLXMLParser::getCharAttribute(DOMElement* element, char* att_name)
{
	const XMLCh *att = element->getAttribute(X(att_name));
	char* str = XMLString::transcode(att);

	char* r_str = NULL;
	int len = strlen(str);
	r_str = new char[len+1];
	memcpy(r_str,str,len);
	r_str[len] = 0;
	XMLString::release(&str);
	
	return r_str;
}

DOMNodeList* LLXMLParser::getElements(char* node_name)
{
	if (m_pDoc == NULL)
		return NULL;
	
	DOMNodeList* node_list = m_pDoc->getElementsByTagName(X(node_name));

	return node_list;
}

DOMNodeList* LLXMLParser::getChildren(DOMElement* element, char* tag_name)
{
	if (tag_name == NULL)
		return element->getChildNodes();
	else
		return element->getElementsByTagName(X(tag_name));
}

DOMNode* LLXMLParser::getFirstChild(DOMElement* element)
{
	DOMNode* node = element->getFirstChild();
	return node;
}

DOMNode* LLXMLParser::getNextSibling(DOMElement* element)
{
	DOMNode* node = element->getNextSibling();
	return node;
}

void LLXMLParser::makePrettyPrint(DOMNode *node)
{
	remove_ws_nodes(node);
	make_pretty_print(node, 0, 0);
}
